package ltd.flyweight.liquibasehelper.module.anchor.service;

import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.toolkit.SqlRunner;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import ltd.flyweight.liquibasehelper.core.dao.entity.PgColumnsEntity;
import ltd.flyweight.liquibasehelper.core.dao.mapper.PgColumnsMapper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;

import java.io.FileWriter;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Slf4j
@Service
@RequiredArgsConstructor
public class AnchorTableService {

    private final PgColumnsMapper pgColumnsMapper;

    public void runTaskGenInitDataLiquibaseYmlScript(String datasource, String tableName) throws Exception {
        runTaskGenInitDataLiquibaseYmlScript(datasource, tableName);
    }

    public void runTaskGenInitDataLiquibaseYmlScript(String datasource, String tableName, Serializable pk) throws Exception {
        runTaskGenInitDataLiquibaseYmlScript(datasource, tableName, "id", pk);
    }

    public void runTaskGenInitDataLiquibaseYmlScript(String datasource, String tableName, String pkFiled, Serializable pk) throws Exception {
        runTaskGenInitDataLiquibaseYmlScript(datasource, tableName, pkFiled, List.of(pk));
    }

    public void runTaskGenInitDataLiquibaseYmlScript(String datasource, String tableName, Collection<Serializable> pks) throws Exception {
        runTaskGenInitDataLiquibaseYmlScript(datasource, tableName, "id", pks);
    }

    public void runTaskGenInitDataLiquibaseYmlScript(String datasource, String tableName, String pkFiled, Collection<Serializable> pks) throws Exception {
        runTaskGenInitDataLiquibaseYmlScriptWithSchema(datasource, "public", tableName, pkFiled, pks);
    }

    public void runTaskGenInitDataLiquibaseYmlScriptWithSchema(String datasource, String tableSchema, String tableName) throws Exception {
        runTaskGenInitDataLiquibaseYmlScriptWithSchema(datasource, tableSchema, tableName, (Serializable) null);
    }

    public void runTaskGenInitDataLiquibaseYmlScriptWithSchema(String datasource, String tableSchema, String tableName, Serializable pk) throws Exception {
        runTaskGenInitDataLiquibaseYmlScriptWithSchema(datasource, tableName, tableSchema, "id", List.of(pk));
    }

    public void runTaskGenInitDataLiquibaseYmlScriptWithSchema(String datasource, String tableSchema, String tableName, String pkFiled, Serializable pk) throws Exception {
        runTaskGenInitDataLiquibaseYmlScriptWithSchema(datasource, tableName, tableSchema, pkFiled, List.of(pk));
    }

    public void runTaskGenInitDataLiquibaseYmlScriptWithSchema(String datasource, String tableSchema, String tableName, Collection<Serializable> pks) throws Exception {
        runTaskGenInitDataLiquibaseYmlScriptWithSchema(datasource, tableSchema, tableName, "id", pks);
    }

    public void runTaskGenInitDataLiquibaseYmlScriptWithSchema(String datasource, String tableSchema, String tableName, String pkFiled, Collection<Serializable> pks) throws Exception {
        DynamicDataSourceContextHolder.push(datasource);
        try {
            List<PgColumnsEntity> pgColumnsEntityList = pgColumnsMapper.selectList(
                    Wrappers
                            .lambdaQuery(PgColumnsEntity.class)
                            .eq(StringUtils.isNotEmpty(tableSchema), PgColumnsEntity::getTableSchema, "public")
                            .eq(PgColumnsEntity::getTableName, tableName)
                            .orderByAsc(PgColumnsEntity::getOrdinalPosition)
            );
            // 判断为空
            StringBuilder querySqlBuilder = new StringBuilder();
            querySqlBuilder.append("SELECT ");
            for (PgColumnsEntity pgColumnsEntity : pgColumnsEntityList) {
                querySqlBuilder.append(pgColumnsEntity.getColumnName());
                querySqlBuilder.append(",");
            }
            querySqlBuilder.setLength(querySqlBuilder.length() - 1);
            querySqlBuilder.append(" FROM ");
            querySqlBuilder.append(tableSchema);
            querySqlBuilder.append(".");
            querySqlBuilder.append(tableName);
            if (CollectionUtils.isNotEmpty(pks)) {
                querySqlBuilder.append(" WHERE ");
                querySqlBuilder.append(pkFiled);
                querySqlBuilder.append(" IN ({0})");
            }
            String querySql = querySqlBuilder.toString();
            log.info("{}", querySqlBuilder);

            List<Map<String, Object>> queryResult = null;
            if (CollectionUtils.isNotEmpty(pks)) {
                queryResult = SqlRunner.db().selectList(querySql, pks);
            } else {
                queryResult = SqlRunner.db().selectList(querySql);
            }
            log.info("{}", queryResult);

            Map<String, Object> dataMap = new LinkedHashMap<>();

            List<Map<String, Object>> databaseChangeLogList = new LinkedList<>();
            dataMap.put("databaseChangeLog", databaseChangeLogList);

            Map<String, Object> changeSetMap = new LinkedHashMap<>();
            databaseChangeLogList.add(changeSetMap);

            Map<String, Object> changeSetAttrMap = new LinkedHashMap<>();
            changeSetMap.put("changeSet", changeSetAttrMap);

            changeSetAttrMap.put("id", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd-HHmm")) + "-01");
            changeSetAttrMap.put("author", "周立山");
            changeSetAttrMap.put("comment", tableName + "表初始化数据");

            List<Map<String, Object>> changeSetAttrChangesList = new LinkedList<>();
            changeSetAttrMap.put("changes", changeSetAttrChangesList);

            for (Map<String, Object> rowMap : queryResult) {
                Map<String, Object> changeSetAttrChangesInsertMap = new LinkedHashMap<>();
                changeSetAttrChangesList.add(changeSetAttrChangesInsertMap);

                Map<String, Object> changeSetAttrChangesInsertDataMap = new LinkedHashMap<>();
                changeSetAttrChangesInsertMap.put("insert", changeSetAttrChangesInsertDataMap);

                changeSetAttrChangesInsertDataMap.put("tableName", tableName);
                // changeSetAttrChangesInsertDataMap.put("schemaName", tableSchema);

                List<Map<String, Object>> changeSetAttrChangesInsertDataColumnsList = new LinkedList<>();
                changeSetAttrChangesInsertDataMap.put("columns", changeSetAttrChangesInsertDataColumnsList);

                for (PgColumnsEntity pgColumnsEntity : pgColumnsEntityList) {
                    Map<String, Object> changeSetAttrChangesInsertDataColumnsColumnMap = new LinkedHashMap<>();
                    changeSetAttrChangesInsertDataColumnsList.add(changeSetAttrChangesInsertDataColumnsColumnMap);

                    Map<String, Object> changeSetAttrChangesInsertDataColumnsColumnDataMap = new LinkedHashMap<>();
                    changeSetAttrChangesInsertDataColumnsColumnMap.put("column", changeSetAttrChangesInsertDataColumnsColumnDataMap);

                    changeSetAttrChangesInsertDataColumnsColumnDataMap.put("name", pgColumnsEntity.getColumnName());
                    Object ColumnValue = rowMap.get(pgColumnsEntity.getColumnName());
                    if (ColumnValue == null) {
                        changeSetAttrChangesInsertDataColumnsColumnDataMap.put("value", "NULL");
                    } else {
                        changeSetAttrChangesInsertDataColumnsColumnDataMap.put("value", ColumnValue.toString());
                    }
                }
            }

            // 设置DumperOptions，使得输出更加美观
            DumperOptions options = new DumperOptions();
            options.setIndent(4);
            options.setIndicatorIndent(2);
            options.setPrettyFlow(true);
            options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);

            // 初始化YAML处理器
            Yaml yaml = new Yaml(options);

            // 写入文件
            try (FileWriter writer = new FileWriter("init_%s.yml".formatted(tableName))) {
                yaml.dump(dataMap, writer);
            }
        } finally {
            DynamicDataSourceContextHolder.clear();
        }
    }


    private void runTaskGenInitDataSqlScript(String tableName, String tableSchema, List<Map<String, Object>> queryResult) {
        for (Map<String, Object> rowMap : queryResult) {
            StringBuilder insertSqlBuilder = new StringBuilder();
            insertSqlBuilder.append("INSERT INTO ");
            insertSqlBuilder.append(tableSchema);
            insertSqlBuilder.append(".");
            insertSqlBuilder.append(tableName);
            insertSqlBuilder.append(" (");
            for (Map.Entry<String, Object> columnEntry : rowMap.entrySet()) {
                insertSqlBuilder.append(columnEntry.getKey());
                insertSqlBuilder.append(",");
            }
            insertSqlBuilder.setLength(insertSqlBuilder.length() - 1);
            insertSqlBuilder.append(") VALUES (");
            for (Map.Entry<String, Object> columnEntry : rowMap.entrySet()) {
                insertSqlBuilder.append(columnEntry.getValue());
                insertSqlBuilder.append(",");
            }
            insertSqlBuilder.setLength(insertSqlBuilder.length() - 1);
            insertSqlBuilder.append(");");
            log.info("{}", insertSqlBuilder);
        }
    }

}
